<template>
	<view class="uni-file-picker">
		<view v-if="title" class="uni-file-picker__header">
			<text class="file-title">{{ title }}</text>
			<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
		</view>
		<upload-image
			v-if="fileMediatype === 'image' && showType === 'grid'"
			:readonly="readonly"
			:image-styles="imageStyles"
			:files-list="filesList"
			:limit="limitLength"
			:disablePreview="disablePreview"
			:delIcon="delIcon"
			@uploadFiles="uploadFiles"
			@choose="choose"
			@delFile="delFile"
		>
			<slot>
				<view class="is-add">
					<view class="icon-add"></view>
					<view class="icon-add rotate"></view>
				</view>
			</slot>
		</upload-image>
		<upload-file
			v-if="fileMediatype !== 'image' || showType !== 'grid'"
			:readonly="readonly"
			:list-styles="listStyles"
			:files-list="filesList"
			:showType="showType"
			:delIcon="delIcon"
			@uploadFiles="uploadFiles"
			@choose="choose"
			@delFile="delFile"
		>
			<slot><button type="primary" size="mini">选择文件</button></slot>
		</upload-file>
	</view>
</template>

<script>
/**
 * FilePicker
 * @description 文件选择上传组件，可以选择图片、视频等任意文件并上传到当前绑定的服务空间
 * @tutorial https://ext.dcloud.net.cn/plugin?id=-1
 * @property {Object|Array}	value	组件数据，通常用来回显 ,类型由return-type属性决定
 * @property {Boolean}	disabled=[true|false]	组件禁用
 * 	@value true 	禁用
 * 	@value false 	取消禁用
 * @property {Boolean}	readonly=[true|false]	组件只读，不可选择，不显示进度，不显示删除按钮
 * 	@value true 	只读
 * 	@value false 	取消只读
 * @property {String}	return-type=[array|object]	限制 value 格式，当为 object 时 ，组件只能单选，且会覆盖
 * 	@value array	规定 value 属性的类型为数组
 * 	@value object	规定 value 属性的类型为对象
 * @property {Boolean}	disable-preview=[true|false]	禁用图片预览，仅 mode:grid 时生效
 * 	@value true 	禁用图片预览
 * 	@value false 	取消禁用图片预览
 * @property {Boolean}	del-icon=[true|false]	是否显示删除按钮
 * 	@value true 	显示删除按钮
 * 	@value false 	不显示删除按钮
 * @property {Boolean}	auto-upload=[true|false]	是否自动上传，值为true则只触发@select,可自行上传
 * 	@value true 	自动上传
 * 	@value false 	取消自动上传
 * @property {Number|String}	limit	最大选择个数 ，h5 会自动忽略多选的部分
 * @property {String}	title	组件标题，右侧显示上传计数
 * @property {String}	mode=[list|grid]	选择文件后的文件列表样式
 * 	@value list 	列表显示
 * 	@value grid 	宫格显示
 * @property {String}	file-mediatype=[image|video|all]	选择文件类型
 * 	@value image	只选择图片
 * 	@value video	只选择视频
 * 	@value all		选择所有文件
 * @property {Array}	file-extname	选择文件后缀，根据 file-mediatype 属性而不同
 * @property {Object}	list-style	mode:list 时的样式
 * @property {Object}	image-styles	选择文件后缀，根据 file-mediatype 属性而不同
 * @event {Function} select 	选择文件后触发
 * @event {Function} progress 文件上传时触发
 * @event {Function} success 	上传成功触发
 * @event {Function} fail 		上传失败触发
 * @event {Function} delete 	文件从列表移除时触发
 */

import { chooseAndUploadFile } from './choose-and-upload-file.js';
import uploadImage from './upload-image.vue';
import uploadFile from './upload-file.vue';
let fileInput = null;
export default {
	name: 'uniUpload',
	components: {
		uploadImage,
		uploadFile
	},
	props: {
		value: {
			type: [Array, Object],
			default() {
				return [];
			}
		},
		disabled: {
			type: Boolean,
			default: false
		},
		disablePreview: {
			type: Boolean,
			default: false
		},
		delIcon: {
			type: Boolean,
			default: true
		},
		// 自动上传
		autoUpload: {
			type: Boolean,
			default: true
		},
		// 最大选择个数 ，h5只能限制单选或是多选
		limit: {
			type: [Number, String],
			default: 9
		},
		// 列表样式 grid | list | list-card
		mode: {
			type: String,
			default: 'grid'
		},
		// inputUrl: {
		// 	type: Boolean,
		// 	default: false
		// },
		// 选择文件类型  image/video/all
		fileMediatype: {
			type: String,
			default: 'image'
		},
		// 文件类型筛选
		fileExtname: {
			type: Array,
			default() {
				return [];
			}
		},
		title: {
			type: String,
			default: ''
		},
		listStyles: {
			type: Object,
			default() {
				return {
					// 是否显示边框
					border: true,
					// 是否显示分隔线
					dividline: true,
					// 线条样式
					borderStyle: {}
				};
			}
		},
		imageStyles: {
			type: Object,
			default() {
				return {
					width: 'auto',
					height: 'auto'
				};
			}
		},
		readonly: {
			type: Boolean,
			default: false
		},
		returnType: {
			type: String,
			default: 'array'
		}
	},
	watch: {
		value: {
			handler(newVal) {
				let newFils = [];
				let newData = [].concat(newVal || []);
				newData.forEach(v => {
					const files = this.files.find(i => i.url === v.url);
					const reg = /cloud:\/\/([\w.]+\/?)\S*/;
					if (!v.path) {
						v.path = v.url;
					}
					if (reg.test(v.url)) {
						this.getTempFileURL(v, v.url);
					}
					newFils.push(files ? files : v);
				});
				this.files = newFils;
			},
			immediate: true
		}
	},
	data() {
		return {
			files: []
		};
	},
	computed: {
		filesList() {
			let files = [];
			this.files.forEach(v => {
				files.push(v);
			});
			console.log('哈哈啊哈’', this.files);
			return files;
		},
		showType() {
			if (this.fileMediatype === 'image') {
				return this.mode;
			}
			return 'list';
		},
		limitLength() {
			if (this.returnType === 'object') {
				return 1;
			}
			if (!this.limit) {
				return 1;
			}
			if (this.limit >= 9) {
				return 9;
			}
			return this.limit;
		}
	},
	created() {
		this.files = Object.assign([], this.value);
		this.tempData = {};
	},
	methods: {
		/**
		 * 继续上传
		 */
		upload() {
			// TODO 先放弃这个实现 ，不能全部上传
			// if (this.$uploadFiles) {
			// 	this.$uploadFiles()
			// } else {
			// 	uni.showToast({
			// 		title: '请先选择文件',
			// 		icon: 'none'
			// 	})
			// }
			let files = [];
			this.files.forEach((v, index) => {
				if (v.status === 'ready' || v.status === 'error') {
					files.push(Object.assign({}, v));
				}
			});

			this.uploadFiles(files);
		},
		/**
		 * 选择文件
		 */
		choose() {
			if (this.disabled) return;
			if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === 'array') {
				uni.showToast({
					title: `您最多选择 ${this.limitLength} 个文件`,
					icon: 'none'
				});
				return;
			}
			// uni.showActionSheet({
			// 	itemList: ['填写 url 地址', '选择文件'],
			// 	success: (res) => {
			// 		if (res.tapIndex === 1) {
			// 			this.chooseFiles()
			// 		}
			// 	},
			// 	fail: function(res) {}
			// });
			this.chooseFiles();
		},

		/**
		 * 选择文件并上传
		 */
		chooseFiles() {
			// API 正式发布前，使用本地API上传函数
			if (!uniCloud.chooseAndUploadFile) {
				uniCloud.chooseAndUploadFile = chooseAndUploadFile;
			}

			uniCloud
				.chooseAndUploadFile({
					type: this.fileMediatype,
					compressed: false,
					// TODO 如果为空，video 有问题
					extension: this.fileExtname.length > 0 ? this.fileExtname : undefined,
					count: this.limitLength - this.files.length, //默认9
					onChooseFile: async res => {
						console.log(res, '上传信息');
						if ((Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) || this.returnType === 'object') {
							this.files = [];
						}
						let filePaths = [];
						let files = [];
						if (this.fileExtname && this.fileExtname.length > 0) {
							res.tempFiles.forEach(v => {
								let fileFullName = this.getFileExt(v.name);
								const extname = fileFullName.ext.toLowerCase();
								if (this.fileExtname.indexOf(extname) !== -1) {
									files.push(v);
									filePaths.push(v.path);
								}
							});
							if (files.length !== res.tempFiles.length) {
								uni.showToast({
									title: `当前选择了${res.tempFiles.length}个文件 ，${res.tempFiles.length - files.length} 个文件格式不正确`,
									icon: 'none',
									duration: 5000
								});
							}
						} else {
							filePaths = res.tempFilePaths;
							files = res.tempFiles;
						}

						let currentData = [];
						for (let i = 0; i < files.length; i++) {
							if (this.limitLength - this.files.length <= 0) break;
							files[i].uuid = Date.now();
							let filedata = await this.getFileData(files[i], this.fileMediatype);
							filedata.progress = 0;
							filedata.status = 'ready';
							this.files.push(filedata);
							currentData.push(filedata);
						}
						this.$emit('select', {
							tempFiles: currentData,
							tempFilePaths: filePaths
						});

						// 停止自动上传
						if (!this.autoUpload) {
							res.tempFiles = [];
							// TODO 先放弃这个实现 ，不能全部上传
							// return new Promise((resolve) => {
							// 	this.$uploadFiles = () => {
							// 		// this._is_uploading = true
							// 		resolve(res)
							// 	}
							// })
						}
					},
					onUploadProgress: progressEvent => {
						this.setProgress(progressEvent, progressEvent.index);
					}
				})
				.then(result => {
					this.setSuccessAndError(result.tempFiles);
				})
				.catch(err => {
					console.log('选择失败', err);
				});
		},

		/**
		 * 批传
		 * @param {Object} e
		 */
		uploadFiles(files) {
			files = [].concat(files);
			this.uploadCloudFiles(files, 5, res => {
				this.setProgress(res, res.index, true);
			})
				.then(result => {
					this.setSuccessAndError(result);
				})
				.catch(err => {
					console.log('err', err);
				});
		},

		/**
		 * 成功或失败
		 */
		async setSuccessAndError(res, fn) {
			console.log(res, '上传信息');
			let successData = [];
			let errorData = [];
			let tempFilePath = [];
			let errorTempFilePath = [];
			for (let i = 0; i < res.length; i++) {
				// const index  = item.index
				const item = res[i];
				const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index;
				if (index === -1 || !this.files) break;
				if (item.errMsg === 'request:fail') {
					this.files[index].url = item.path;
					this.files[index].status = 'error';
					this.files[index].errMsg = item.errMsg;
					this.files[index].progress = -1;
					errorData.push(this.files[index]);
					errorTempFilePath.push(this.files[index].url);
				} else {
					this.files[index].errMsg = '';
					this.files[index].url = item.url;
					this.files[index].status = 'success';
					successData.push(this.files[index]);
					tempFilePath.push(this.files[index].url);
				}
			}

			if (successData.length > 0) {
				this.setEmit();
				// 状态改变返回
				this.$emit('success', {
					tempFiles: this.backObject(successData),
					tempFilePaths: tempFilePath
				});
			}

			if (errorData.length > 0) {
				this.$emit('fail', {
					tempFiles: this.backObject(errorData),
					tempFilePaths: errorTempFilePath
				});
			}
		},

		/**
		 * 获取进度
		 * @param {Object} progressEvent
		 * @param {Object} index
		 * @param {Object} type
		 */
		setProgress(progressEvent, index, type) {
			const fileLenth = this.files.length;
			const percentNum = (index / fileLenth) * 100;
			const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
			let idx = index;
			if (!type) {
				idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid);
			}
			if (idx === -1 || !this.files[idx]) return;
			this.files[idx].progress = percentCompleted;
			// 上传中
			this.$emit('progress', {
				index: idx,
				progress: parseInt(percentCompleted),
				tempFile: this.files[idx]
			});
		},

		/**
		 * 删除
		 * @param {Object} index
		 */
		delFile(index) {
			this.$emit('delete', {
				tempFile: this.files[index],
				tempFilePath: this.files[index].url
			});
			this.files.splice(index, 1);
		},

		/**
		 * 获取文件名和后缀
		 * @param {Object} name
		 */
		getFileExt(name) {
			const last_len = name.lastIndexOf('.');
			const len = name.length;
			return {
				name: name.substring(0, last_len),
				ext: name.substring(last_len + 1, len)
			};
		},

		/**
		 * 获取图片信息
		 * @param {Object} filepath
		 */
		getFileInfo(filepath) {
			return new Promise((resolve, reject) => {
				uni.getImageInfo({
					src: filepath,
					success(res) {
						resolve(res);
					},
					fail(err) {
						reject(err);
					}
				});
			});
		},

		/**
		 * 获取封装数据
		 */
		async getFileData(files, type = 'image') {
			console.log(files, '哈哈哈');
			// 最终需要上传数据库的数据
			let fileFullName = this.getFileExt(files.name);
			const extname = fileFullName.ext.toLowerCase();
			let filedata = {
				name: files.name,
				uuid: files.uuid,
				extname: extname || '',
				cloudPath: files.cloudPath,
				fileType: files.fileType,
				url: files.path || files.path,
				size: files.size, //单位是字节
				image: {},
				path: files.path,
				duration: files.duration || 0,
				video: {}
			};
			if (type === 'image') {
				const imageinfo = await this.getFileInfo(files.path);
				filedata.image.width = imageinfo.width;
				filedata.image.height = imageinfo.height;
				filedata.image.location = imageinfo.path;
			}
			return filedata;
		},

		/**
		 * 批量上传
		 */
		uploadCloudFiles(files, max = 5, onUploadProgress) {
			files = JSON.parse(JSON.stringify(files));
			const len = files.length;
			let count = 0;
			let self = this;
			return new Promise(resolve => {
				while (count < max) {
					next();
				}

				function next() {
					let cur = count++;
					if (cur >= len) {
						!files.find(item => !item.url && !item.errMsg) && resolve(files);
						return;
					}
					const fileItem = files[cur];
					const index = self.files.findIndex(v => v.uuid === fileItem.uuid);
					fileItem.url = '';
					delete fileItem.errMsg;

					uniCloud
						.uploadFile({
							filePath: fileItem.path,
							cloudPath: fileItem.cloudPath,
							fileType: fileItem.fileType,
							onUploadProgress: res => {
								res.index = index;
								onUploadProgress && onUploadProgress(res);
							}
						})
						.then(res => {
							fileItem.url = res.fileID;
							fileItem.index = index;
							if (cur < len) {
								next();
							}
						})
						.catch(res => {
							fileItem.errMsg = res.errMsg || res.message;
							fileItem.index = index;
							if (cur < len) {
								next();
							}
						});
				}
			});
		},
		setEmit() {
			let data = [];
			if (this.returnType === 'object') {
				data = this.backObject(this.files)[0];
			} else {
				data = this.backObject(this.files);
			}
			this.$emit('input', data);
		},
		backObject(files) {
			let newFilesData = JSON.parse(JSON.stringify(files));
			newFilesData.map(v => {
				delete v.path;
				delete v.uuid;
				delete v.video;
				delete v.progress;
				delete v.errMsg;
				delete v.status;
				delete v.cloudPath;
				return v;
			});
			return newFilesData;
		},
		async getTempFileURL(file, fileList) {
			fileList = {
				fileList: [].concat(fileList)
			};
			const urls = await uniCloud.getTempFileURL(fileList);
			file.path = urls.fileList[0].tempFileURL || '';
			const index = this.files.findIndex(v => v.path === file.path);
			if (index !== -1) {
				this.$set(this.files, index, file);
			}
		}
	}
};
</script>

<style>
.uni-file-picker {
	/* #ifndef APP-NVUE */
	box-sizing: border-box;
	overflow: hidden;
	/* #endif */
}

.uni-file-picker__header {
	padding-top: 5px;
	padding-bottom: 10px;
	display: flex;
	justify-content: space-between;
}

.file-title {
	font-size: 14px;
	color: #333;
}

.file-count {
	font-size: 14px;
	color: #999;
}

.is-add {
	display: flex;
	align-items: center;
	justify-content: center;
}

.icon-add {
	width: 50px;
	height: 5px;
	background-color: #f1f1f1;
	border-radius: 2px;
}

.rotate {
	position: absolute;
	transform: rotate(90deg);
}
</style>
